{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python Introduction:\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Types"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's evaluate some simple expressions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#clear\n",
    "3*2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "11"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#clear\n",
    "5+3*2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can use `type()` to find the *type* of an expression."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "int"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#clear\n",
    "type(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now add decimal points."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "12.0"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#clear\n",
    "5+3.5*2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "float"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#clear\n",
    "type(5+3.0*2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Strings are written with single (``'``) or double quotes (`\"`)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"hello 'hello'\""
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'hello \"hello\"'\n",
    "\n",
    "\"hello 'hello'\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Multiplication and addition work on strings, too."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'hello hello hello cs357'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#clear\n",
    "3 * 'hello ' + \"cs357\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lists are written in brackets (`[]`) with commas (`,`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[5, 3.5, 7]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#clear\n",
    "[5, 3.5, 7]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "list"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#clear \n",
    "type([5,3,7])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "List entries don't have to have the same type."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['hi there', 15, [1, 2, 3]]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#clear\n",
    "[\"hi there\", 15, [1,2,3]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\"Multiplication\" and \"addition\" work on lists, too."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 5, 5, 5]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#clear\n",
    "[1,2,3] * 4 + [5, 5, 5]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hmmmmmm. Was that what you expected? How can we \"fix\" this?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 9, 13, 17])"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# clear\n",
    "import numpy as np\n",
    "\n",
    "np.array([1,2,3]) * 4 + np.array([5,5,5])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We will introduce python later in this notebook"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Names and Values"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Define and reference a variable:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#clear\n",
    "a = 3*2 + 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#clear\n",
    "a = \"interesting\"*3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "No type declaration needed!\n",
    "\n",
    "(But values still have types--let's check.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#clear\n",
    "type(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Everything in python is an object. Python variables are like *pointers*.\n",
    "\n",
    "(if that word makes sense)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a = [1,2,3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "b = a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#clear\n",
    "print(a)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#clear\n",
    "b.append(4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#clear\n",
    "b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#clear\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can see this pointer with `id()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#clear\n",
    "print(id(a), id(b))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `is` operator tests for object sameness."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#clear\n",
    "a is b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is a **stronger** condition than being equal!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = [1,2,3]\n",
    "b = [1,2,3]\n",
    "print(\"IS   \", a is b)\n",
    "print(\"EQUAL\", a == b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What do you think the following prints?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = [1,2,3]\n",
    "b = a\n",
    "a = a + [4]\n",
    "print(b)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a is b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Why is that?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "-----\n",
    "How could this lead to bugs?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "----------\n",
    "* To help manage this risk, Python provides **immutable** types.\n",
    "\n",
    "* Immutable types cannot be changed in-place, only by creating a new object.\n",
    "\n",
    "* A `tuple` is an immutable `list`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a = [1,2,3]\n",
    "type(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a[2] = 0\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#clear\n",
    "a = (1,2,3)\n",
    "type(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's try to change that tuple."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# clear\n",
    "a[2] = 0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Bonus question:* How do you spell a single-element tuple?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#clear\n",
    "a = (3,)\n",
    "\n",
    "type(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "String is also immutable type. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "myName = 'Mariena'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that myName is spelled incorrectly. We would like to change the letter \"e\" with a letter \"a\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myName[4]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myName[4]='a'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Memory management"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = \"apple\"\n",
    "b = \"apple\"\n",
    "print(id(a),id(b))\n",
    "print (a is b)\n",
    "print (a == b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"figures/PointToString.png\" width=200 />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that \"a\" and \"b\" are bounded to the same object \"apple\", and therefore they have the same id. For optimization reasons, Python does not store duplicates of \"simple\" strings. But if the strings are a little more complicated..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = \"Hello, how are you?\"\n",
    "b = \"Hello, how are you?\"\n",
    "print(id(a),id(b))\n",
    "print (a is b)\n",
    "print (a == b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is called interning, and Python does interning (to some extent) of shorter string literals (such as \"apple\") which are created at compile time. But in general, Python string literals creates a new string object each time (as in \"Hello, how are you?\"). Interning is runtime dependant and is always a trade-off between memory use and the cost of checking if you are creating the same string. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In general, integers, floats, lists, tuples, etc, will be stored at different locations, and therefore have different ids."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = 5000\n",
    "b = 5000\n",
    "print(id(a),id(b))\n",
    "print (a is b)\n",
    "print (a == b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = (1,[2,3],4)\n",
    "b = (1,[2,3],4)\n",
    "print(id(a),id(b))\n",
    "print (a is b)\n",
    "print (a == b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Also for optimization reasons, Python will <strong>not</strong> duplicate integers between -5 and 256. Instead, it will  keep an array of integer objects for all integers between -5 and 256, so when you create an int in that range you actually just get back a reference to the existing object. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "a = 256\n",
    "b = 256\n",
    "print(id(a),id(b))\n",
    "print (a is b)\n",
    "print (a == b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = 257\n",
    "b = 257\n",
    "print(id(a),id(b))\n",
    "print (a is b)\n",
    "print (a == b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "http://foobarnbaz.com/2012/07/08/understanding-python-variables/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Objects and Naming\n",
    "\n",
    "Understanding objects and naming in Python can be difficult.  In this demo we'll follow a post [Is Python call-by-value or call-by-reference? Neither.](https://jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-or-callbyreference-neither/)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First, let's make some objects.  *Everything in Python is an object*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#So when we make a string it's an object. When we call it a variable name, it binds that name to the string object. \n",
    "fruit = 'apple'\n",
    "\n",
    "#When we make a list, it will point to the object bound by fruit\n",
    "lunch = []\n",
    "lunch.append(fruit)\n",
    "\n",
    "dinner = lunch\n",
    "dinner.append('fish')\n",
    "\n",
    "fruit = 'pear'\n",
    "\n",
    "meals = [fruit, lunch, dinner]\n",
    "print(meals)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's check the object ids for both lists"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(id(lunch))\n",
    "print(id(dinner))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice what happens when we append to list that is bound to both `lunch` and `dinner`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dinner.append('pasta')\n",
    "print(lunch, dinner)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "lunch.append('carrots')\n",
    "print(lunch,dinner)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Mutable and Immutable\n",
    "\n",
    "We've looked at mutable and immutable.  Tuples are an example of immutable objects."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fruits = ['apple', 'banana', 'orange']\n",
    "veggies = ['carrot', 'broccoli']\n",
    "\n",
    "food_tuple = (fruits, veggies)\n",
    "\n",
    "\n",
    "fruits.append('plum')\n",
    "\n",
    "print(fruits)\n",
    "\n",
    "print(food_tuple)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Indexing and Slicing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `range` function lets us build a list of numbers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(range(1,10))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(range(10, 20,1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice anything funny?\n",
    "\n",
    "Python uses this convention everywhere."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = list(range(10, 20))\n",
    "type(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's talk about indexing.\n",
    "\n",
    "Indexing in Python starts at 0."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And goes from there."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a[1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a[2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What do negative numbers do?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a[-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a[-2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can get a sub-list by *slicing*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(a)\n",
    "print(a[3:7])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Start and end are optional."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a[3:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a[:3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Again, notice how the end entry is not included:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "print(a[:3])\n",
    "print(a[3])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Slicing works on any sequence type! (`list`, `tuple`, `str`, `numpy` array)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a = \"CS357\"\n",
    "a[-3:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#clear\n",
    "a = [0,1,2,3,4,5,6,7,8,9]\n",
    "a[1::2][::-1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Control Flow"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`for` loops in Python always iterate over something list-like:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in range(3,10):\n",
    "\n",
    "    print(i)\n",
    "    \n",
    "    \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note** that Python does block-structuring by leading spaces.\n",
    "\n",
    "Also note the trailing \"`:`\"."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "`if`/`else` are as you would expect them to be:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "for i in range(10):\n",
    "    if i % 3 == 0:\n",
    "        print(\"{0} is divisible by 3\".format(i))\n",
    "    else:\n",
    "        print(\"{0} is not divisible by 3\".format(i))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "print(\"My name is %s\" % 'Luke')\n",
    "print(\"My name is {}\".format('Luke'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`while` loops exist too:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "i = 0\n",
    "while True:\n",
    "    i += 1\n",
    "    if i**3 + i**2 + i + 1 == 3616:\n",
    "        break\n",
    "\n",
    "print(\"SOLUTION:\", i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "----\n",
    "Building lists by hand can be a little long. For example, build a list of the squares of integers below 50 divisible by 7:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "mylist = []\n",
    "\n",
    "for i in range(50):\n",
    "\n",
    "    if i % 7 == 0:\n",
    "\n",
    "        mylist.append(i**2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "mylist"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python has a something called *list comprehension*:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mylist = [i**2 for i in range(50) if i % 7 == 0]\n",
    "print(mylist)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Enumerate"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "suppose you had a single list:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0: the fruit is apples\n",
      "1: the fruit is bananas\n",
      "2: the fruit is oranges\n",
      "3: the fruit is grapes\n"
     ]
    }
   ],
   "source": [
    "fruits = ['apples', 'bananas', 'oranges', 'grapes']\n",
    "for i, f in enumerate(fruits):\n",
    "    print(\"%d: the fruit is %s\" % (i,f))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Zip (another helpful function)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('a', 'apples')\n",
      "('b', 'bananas')\n",
      "('c', 'oranges')\n",
      "('d', 'grapes')\n"
     ]
    }
   ],
   "source": [
    "ids = ['a', 'b', 'c', 'd']\n",
    "fruits = ['apples', 'bananas', 'oranges', 'grapes']\n",
    "for c in zip(ids, fruits):\n",
    "    print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dictionaries"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dictionaries have *key*:*value* pairs of any Python object:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#mydict = {key: value}\n",
    "mydict = {'Luke': 15,'Mariana' : 22}\n",
    "print(mydict[\"Mariana\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "string = \"Batman\"\n",
    "mydict = {key:ord(key) for key in string}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "print(mydict)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Function definitions\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Functions help extract out common code blocks.\n",
    "\n",
    "Let's define a function `print_greeting()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def print_greeting():\n",
    "    print(\"Hi there, how are you?\")\n",
    "    print(\"Long time no see.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And call it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print_greeting()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That's a bit impersonal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def print_greeting(name):\n",
    "\n",
    "    print(\"Hi there, {0}, how are you?\".format(name))\n",
    "\n",
    "    print(\"Long time no see.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print_greeting(\"Andreas\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print_greeting()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But we might not know their name. So we can set a default value for parameters.\n",
    "\n",
    "(And we just changed the interface of `print_greeting`!)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def print_greeting(name=\"my friend\"):\n",
    "\n",
    "    print(\"Hi there, {0}, how are you?\".format(name))\n",
    "\n",
    "    print(\"Long time no see.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print_greeting(\"Tim\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the order of the parameters does not matter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def printinfo( name , age ):\n",
    "    print(\"Name: \", name)\n",
    "    print(\"Age: \", age)\n",
    "\n",
    "printinfo(40,\"Mariana\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "printinfo( age=8, name=\"Julia\" )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However the parameters \"age\" and \"name\" are both required above. What if we want to have optional parameters (without setting default values)?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def printinfo( firstvar , *othervar ):\n",
    "    print(\"First parameter:\", firstvar)\n",
    "    print(\"List of other parameters:\")\n",
    "    for var in othervar:\n",
    "        print(var)\n",
    "\n",
    "# Now you can call printinfo function\n",
    "printinfo(10,20,30,50,60)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Packing and Unpacking"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def give_me_fruits():\n",
    "    return ['apple', 'banana', 'orange', 'plum']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['apple', 'banana', 'orange']\n"
     ]
    }
   ],
   "source": [
    "*myfruits, _ = give_me_fruits()\n",
    "print(myfruits)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Above we used `_` to denote a return that we want to just ignore (and not bind to a name)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def some_fruits(fruit, *morefruits):\n",
    "    print('The best fruit is %s' % fruit)\n",
    "    for f in morefruits:\n",
    "        print('...not %s' % f)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The best fruit is apple\n",
      "...not banana\n",
      "...not orange\n",
      "...not plum\n"
     ]
    }
   ],
   "source": [
    "some_fruits('apple', 'banana', 'orange', 'plum')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A function can also return more than one parameter, and the results appear as a tuple:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def average_total(a,b):\n",
    "    totalsum = a + b\n",
    "    average = totalsum/2\n",
    "    diff = a - b\n",
    "    return average,totalsum, diff\n",
    "\n",
    "#We can unpack values returned from a function\n",
    "ave,tot,diff = average_total(3,5)\n",
    "print(ave, tot, diff)\n",
    "\n",
    "#We can unpack values using the star mark (*) for variable length\n",
    "ave, *othervar, diff = average_total(3,5)\n",
    "print(ave, diff)\n",
    "\n",
    "ave, *_ = average_total(3,5)\n",
    "print(ave)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Remember mutable and immutable types..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Function parameters work like variables. So what does this do?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def my_func(my_list):\n",
    "    my_list.append(5)\n",
    "    print(\"List printed inside the function: \",my_list)\n",
    "    \n",
    "numberlist = [1,2,3]\n",
    "print(\"List before function call: \",numberlist)\n",
    "my_func(numberlist)\n",
    "print(\"List after function call: \",numberlist)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Can be very surprising! Here, we are maintaining reference of the passed object and appending values in the same object.\n",
    "\n",
    "Define a better function `my_func_2`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def my_func_2(my_list):\n",
    "    \n",
    "    my_list = my_list + [5]\n",
    "    print(\"List printed inside the function: \",my_list)\n",
    "\n",
    "    return my_list\n",
    "\n",
    "\n",
    "numberlist = [1,2,3]\n",
    "print(\"List before function call: \",numberlist)\n",
    "new_list = my_func_2(numberlist)\n",
    "#inside the function my_list = [1,2,3,5]\n",
    "print(\"List after function call: \",numberlist)\n",
    "print(\"Modified list after function call: \",new_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the parameter my_list is local to the function. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def change_fruits(fruit):\n",
    "    fruit='apple'\n",
    "    print(\"I'm changing the fruit to %s\" % fruit)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "myfruit = 'banana'\n",
    "change_fruits(myfruit)\n",
    "print(\"The fruit is %s \" % myfruit)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**What happened?!** Remember that the input, `fruit` to `change_fruits` is bound to an object within the scope of the function:\n",
    "  * If the object is mutable, the object will change\n",
    "  * If the object is immutable (like a string!), then a new object is formed, only to live within the function scope."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Try to answer before running the code snippet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "def add_minor(person):\n",
    "    person.append('math')\n",
    "\n",
    "def switch_majors(person):\n",
    "    person = ['physics']\n",
    "    person.append('economics')\n",
    "    return(person)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# What are the values of Tim and John?\n",
    "John = ['computer_science']\n",
    "Tim = John\n",
    "print(Tim,John)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# What are the values of Tim and John?\n",
    "add_minor(Tim)\n",
    "print(Tim,John)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# What are the values of Tim and John?\n",
    "switch_majors(John)\n",
    "print(Tim,John)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Numpy Introduction:\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## A Difference in Speed"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's import the `numpy` module."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "n = 5  # CHANGE ME\n",
    "a1 = list(range(n)) # python list\n",
    "a2 = np.arange(n)   # numpy array\n",
    "\n",
    "if n <= 10:\n",
    "    print(a1)\n",
    "    print(a2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%timeit [i**2 for i in a1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%timeit a2**2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Numpy Arrays: much less flexible, but:\n",
    "\n",
    "* much faster\n",
    "* less memory"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating a numpy array\n",
    "\n",
    "* Casting from a list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a = np.array([1,2,3,5])\n",
    "print(a)\n",
    "print(a.dtype)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "b = np.array([1.0,2.0,3.0])\n",
    "print(b)\n",
    "print(b.dtype)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But also noticed that:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "c = np.array([1,2,3])\n",
    "print(c)\n",
    "print(c.dtype)\n",
    "\n",
    "d = np.array([1,2.,3])\n",
    "print(d)\n",
    "print(d.dtype)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* `linspace`\n",
    "* np.linspace(start, stop, num=50,...)\n",
    "* num is the number of sample points"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "np.linspace(-1, 1, 9)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* `zeros`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "np.zeros((10,10), np.float64)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create 2D arrays, using zeros, using reshape and from list"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Operations on arrays\n",
    "\n",
    "These propagate to all elements:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a = np.array([1.2, 3, 4])\n",
    "b = np.array([0.5, 0, 1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Addition, multiplication, power ... are all elementwise:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a+b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a*b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a**b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Important Attributes\n",
    "\n",
    "Numpy arrays have two (most) important attributes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "A = np.random.rand(5, 4, 3)\n",
    "A.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `.shape` attribute contains the dimensionality array as a tuple. So the tuple `(5,4,3)` means that we're dealing with a three-dimensional array of size $5 \\times 4 \\times 3$.\n",
    "\n",
    "(`numpy.random.rand` just generates an array of random numbers of the given shape.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "A.dtype"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Other `dtype`s include `np.complex64`, `np.int32`, ..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Try to answer before running the code snippet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a = np.array([2,2,3])\n",
    "b = np.array([1,3,2])\n",
    "\n",
    "# What is the result of the following computation?\n",
    "c = (a+b)*a**b"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "A) c = [ 6 40 45]\n",
    "B) c = [ 6 1000  225]\n",
    "C) c = [ 6 30 30]\n",
    "D) error message"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1D arrays"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = np.random.rand(5)\n",
    "a.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a = np.array([2,3,5])\n",
    "print(a)\n",
    "print(a.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2D arrays"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = np.array([[2],[3],[5]])\n",
    "print(a)\n",
    "print(a.shape)\n",
    "\n",
    "a = np.array([[2,3,5]])\n",
    "print(a)\n",
    "print(a.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can change 1D numpy arrays into 2D numpy arrays using the function `reshape`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = np.array([2,3,5]).reshape(3,1)\n",
    "print(a)\n",
    "print(a.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = np.array([2,3,5]).reshape(1,3)\n",
    "print(a)\n",
    "print(a.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(np.arange(1,10))\n",
    "B = np.arange(1,10).reshape(3,3)\n",
    "print(B)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Transpose"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "print(B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "print(B.transpose())\n",
    "print(B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "print(B.swapaxes(0,1))\n",
    "print(B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "print(B.T)\n",
    "print(B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "C = np.transpose(B)\n",
    "print(C)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What happens when we try to take the transpose of 1D array?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = np.array([[2,3,5]])\n",
    "print(a.T)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But it works with 2D arrays"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = np.array([2,3,5]).reshape(3,1)\n",
    "print(a)\n",
    "print(a.T)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Inner and outer products"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Matrix multiplication is `np.dot(A, B)` for two 2D arrays."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "A = np.random.rand(3, 2)\n",
    "B = np.random.rand(2, 4)\n",
    "C = np.dot(A,B)\n",
    "print(C.shape)\n",
    "\n",
    "b = np.array([5,6])\n",
    "\n",
    "d = np.dot(A,b)\n",
    "print(d.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "A = np.array([[1,3],[2,4]])\n",
    "B = np.array([[2,1],[3,2]])\n",
    "print(np.dot(A,B))\n",
    "print(A@B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a = np.array([1,2,3])\n",
    "b = np.array([5,6,7])\n",
    "#Inner Product\n",
    "print(np.dot(a,b))\n",
    "print(np.inner(a,b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#Outer Product C[i,j] = a[i]*b[j]\n",
    "C = np.outer(a,b)\n",
    "print(np.shape(C))\n",
    "print(C)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Try to answer before running the code snippet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "#clear\n",
    "A = np.array([[2,1],[1,3]])\n",
    "B = np.array([[1,3],[2,2]])\n",
    "c = np.array([[1,2]])\n",
    "d = np.array([[1],[2]])\n",
    "c = np.array([1,2])\n",
    "\n",
    "# What is the output of A@B?"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "#clear\n",
    " A) [[2 3]\n",
    "     [2 6]]\n",
    "\n",
    " B) [[4 8]\n",
    "     [7 9]]\n",
    "\n",
    " C) [[2 2]\n",
    "     [3 6]]\n",
    "\n",
    " D) [[5 6]\n",
    "     [10 8]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[4 8]\n",
      " [7 9]]\n",
      "[[4 8]\n",
      " [7 9]]\n"
     ]
    }
   ],
   "source": [
    "print(np.dot(A,B))\n",
    "print(A@B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[4]\n",
      " [7]]\n",
      "[4 7]\n"
     ]
    }
   ],
   "source": [
    "print(np.dot(A,d))\n",
    "print(A@c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# numpy: Indexing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "A = np.array([[1, 4, 9], [2, 8, 18]])\n",
    "print(A)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "A[1,2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What's the result of this?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "A[:,1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And this?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "A[1:,:1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "One more:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "A[:,[0,2]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Try to answer before running the code snippet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'int'>\n"
     ]
    }
   ],
   "source": [
    "myList = [1,2,3,4,5,6.5]\n",
    "print(type(myList[0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "vec1 = np.array(myList)\n",
    "# What is the type of vec1[0]?"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "Select the correct output:\n",
    "\n",
    "A) <class 'int'>\n",
    "\n",
    "B) <class 'float'>\n",
    "\n",
    "C) <class 'numpy.int64'>\n",
    "\n",
    "D) <class 'numpy.float64'>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'numpy.float64'>\n"
     ]
    }
   ],
   "source": [
    "print(type(vec1[0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#clear\n",
    "A = np.array([[1, 4, 9, 3], [2, 8, 3, 18], [4, 5, 1, 2], [6, 4, 6, 3]])\n",
    "print(A)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "# What is the output of A[1:2,2:]?\n",
    "\n",
    " A) [[ 8  3 18]\n",
    "     [ 5  1  2]]\n",
    "\n",
    " B) [[ 3 18]\n",
    "     [ 1  2]]    \n",
    "\n",
    " C) [[3 18]]\n",
    "\n",
    " D) [[8 3 18]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = np.random.rand(3,4,2)\n",
    "a.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a[...,1].shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Indexing into numpy arrays usually results in a so-called *view*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a = np.zeros((4,4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's call `b` the top-left $2\\times 2$ submatrix."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "b = a[:2,:2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What happens if we change `b`?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "b[1,0] = 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To decouple `b` from `a`, use `.copy()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "b = b.copy()\n",
    "b[1,1] = 7\n",
    "print(b)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Try to answer before running the code snippet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "#clear\n",
    "A = np.array([[1, 4, 9, 3], [2, 8, 3, 18], [4, 5, 1, 2], [6, 4, 6, 3]])\n",
    "b = A[:,2]\n",
    "print(A)\n",
    "d = b\n",
    "print(np.shape(d))\n",
    "d[2] = 2\n",
    "print(A)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "#clear\n",
    "What is the output of print(A)?\n",
    "\n",
    " A) [[ 1  4  9  3]\n",
    "     [ 2  8  3 18]\n",
    "     [ 4  5  2  2]\n",
    "     [ 6  4  6  3]]\n",
    "\n",
    " B) [[ 1  4  9  3]\n",
    "     [ 2  8  3 18]\n",
    "     [ 4  5  1  2]\n",
    "     [ 6  4  6  3]]\n",
    "     \n",
    " C) none of the above"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "You can also index with boolean arrays:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a = np.random.rand(4,4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a_big = a>0.5\n",
    "a_big"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a[a_big]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Also each index individually:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a_row_sel = [True, True, False, True]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a[a_row_sel,:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "And with index arrays:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "x,y = np.nonzero(a > 0.5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a[(x,y)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# numpy: Broadcasting"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = np.arange(9).reshape(3,3)\n",
    "print(a.shape)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "b = np.arange(4, 4+9).reshape(3, 3)\n",
    "print(b.shape)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a+b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So this is easy and one-to-one.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "What if the shapes do not match?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = np.arange(9).reshape(3, 3)\n",
    "print(a.shape)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "b = np.arange(3)\n",
    "print(b.shape)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What will this do?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a+b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It has *broadcast* along the last axis!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Can we broadcast along the *first* axis?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "c = b.reshape(3, 1)\n",
    "c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(a.shape)\n",
    "print(c.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a+c"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Rules:\n",
    "\n",
    "* Shapes are matched axis-by-axis from last to first.\n",
    "* A length-1 axis can be *broadcast* if necessary."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Try to answer before running the code snippet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#clear\n",
    "A = np.array([[2,4],[1,3]])\n",
    "B = np.array([[1,3],[2,2]])\n",
    "c = np.array([1,2])\n",
    "print(np.shape(c))"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "#clear\n",
    "Mark the correct output for print(B + np.dot(A,c))\n",
    "\n",
    "# A) [[3 5]\n",
    "#     [6  8]]\n",
    "\n",
    "# B) [[ 3 11]\n",
    "#     [ 3  8]]\n",
    "\n",
    "# C) [[11 10]\n",
    "#     [12  9]]\n",
    "\n",
    "# D) [[11 13]\n",
    "#     [9  9]]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(B + np.dot(A,c))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#clear\n",
    "# broadcasting in both axis:\n",
    "a = np.arange(1,5)\n",
    "b = np.arange(3,7)\n",
    "\n",
    "d = b*a.reshape((4,1))\n",
    "print(d)\n",
    "print(np.shape(d))\n",
    "\n",
    "# note this is the same as the outer product\n",
    "print(np.outer(a,b))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## Vectorized computations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def func1(u, v, w):\n",
    "    n = len(u)\n",
    "    for i in range(n):\n",
    "        w[i] = u[i] + 15.2 * v[i]\n",
    "    \n",
    "def func2(u, v, w):\n",
    "    w = u + 15.2 * v\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.83 s ± 135 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
      "32.5 ms ± 395 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
     ]
    }
   ],
   "source": [
    "n = 10**7\n",
    "u = np.random.rand(n)\n",
    "v = np.random.rand(n)\n",
    "w = np.zeros(n)\n",
    "\n",
    "\n",
    "%timeit func1(u,v,w)\n",
    "%timeit func2(u,v,w)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
